#!/usr/bin/python
# -*- coding: UTF-8 -*-
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79:
'''expose.py

a full-screen task-switcher liks Apple's Expose

@author: Jiahua Huang <jhuangjiahua@gmail.com>
@license: LGPLv3+
'''

__version__ = '0.0.1'

import gtk
import gobject
import pango
import wnck
import math
import sys
import thread

WATCH = None

def get_pixbuf_from_wnckwindow(wnckwindow, width=0, height=0, use_icon=False):
    xid = wnckwindow.get_xid()
    xwin = gtk.gdk.window_foreign_new(xid)
    w, h = xwin.get_size()
    pixbuf = None
    if not use_icon:
        pixbuf = gtk.gdk.Pixbuf.get_from_drawable(
            gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, True, 8, w, h),
            xwin,
            xwin.get_colormap(),
            0, 0, 0, 0, w, h)
        pass
    if not pixbuf:
        pixbuf = wnckwindow.get_icon()
    if width and height:
        rw = 1.0 * w / width
        rh = 1.0 * h / height
        r = max(rh, rw)
        width, height = int(w / r), int(h /r)
        pixbuf = pixbuf.scale_simple(width, height, gtk.gdk.InterpType(2))
    return pixbuf

def get_desktop_pixmap(screen):
    '''获取桌面图片
    '''
    window = screen.get_windows_stacked()[0]
    pixbuf = get_pixbuf_from_wnckwindow(window)
    pixmap, mask = pixbuf.render_pixmap_and_mask()
    return pixmap

def get_visible_windows(screen):
    windows = list(screen.get_windows_stacked())
    workspace = screen.get_active_workspace()
    windows = [ window for window in screen.get_windows_stacked()
            if window.is_on_workspace(workspace) and not window.is_skip_tasklist()
               and not window.is_minimized()
            ]
    return windows

class MainWindow(gtk.Window):
    #@TODO: 要考虑窗口管理器无混合的情况
    def __init__(self):
        gtk.Window.__init__(self)
        self.screen = wnck.screen_get_default()
        self.connect('delete-event', lambda *args: self.close())
        self.connect('focus-out-event', lambda *args: self.close())
        pass

    def show(self):
        gobject.idle_add(self.fullscreen)
        gobject.idle_add(self.set_type_hint, gtk.gdk.WINDOW_TYPE_HINT_DOCK)
        gobject.idle_add(self.set_background, self.screen)
        gobject.idle_add(self.do_windows_table, self.screen)
        pass

    def close(self):
        global WATCH
        self.hide()
        if self.child:
            self.child.destroy()
            pass
        if not WATCH:
            gtk.main_quit()
            pass

    def do_windows_table(self, screen):
        windows = get_visible_windows(screen)
        if not windows:
            self.close()
            return
        num = len(windows)
        row = int(math.sqrt(num)+0.999)
        col = row
        _width, _height = screen.get_width(), screen.get_height()
        width = int(_width / row * 0.9)
        height = int(_height / col * 0.9)
        table = gtk.Table()
        x, y = 0, 0
        for window in windows:
            button = gtk.Button()
            button.props.relief = gtk.RELIEF_NONE
            #pixbuf = get_pixbuf_from_wnckwindow(window, width, height, True)
            #image = gtk.image_new_from_pixbuf(pixbuf)
            image = gtk.Image()
            def set_image(image, window, width, height):
                pixbuf = get_pixbuf_from_wnckwindow(window, width, height)
                gobject.timeout_add(100, image.set_from_pixbuf, pixbuf)
                pass
            #thread.start_new_thread(set_image, (image, window, width, height))
            set_image(image, window, width, height)
            label = gtk.Label(window.get_name())
            label.set_ellipsize(pango.ELLIPSIZE_END)
            box = gtk.VBox()
            box.pack_start(label, expand=False, fill=False, padding=0)
            box.pack_start(image, expand=True, fill=True, padding=0)
            button.set_tooltip_text(window.get_name())
            button.add(box)
            button.connect('clicked', 
                    lambda button, window: (window.activate(0), self.close()),
                    window)
            if x > col-1:
                x = 0
                y += 1
            table.attach(button, x, x+1, y, y+1, gtk.EXPAND, gtk.EXPAND)
            x += 1
            pass
        eventbox = gtk.EventBox()
        eventbox.set_visible_window(0)
        eventbox.connect('button-release-event', lambda *args: self.close())
        eventbox.add(table)
        self.add(eventbox)
        self.show_all()
        return

    def set_background(self, screen):
        pixmap = get_desktop_pixmap(screen)
        self.show_all()
        style = self.get_style().copy()
        style.bg_pixmap[gtk.STATE_NORMAL] = pixmap
        self.set_style(style)
        self.show_all()
        pass

def poll_pointer(mainwindow):
    root = gtk.gdk.get_default_root_window()
    def poll(root, mainwindow):
        if mainwindow and mainwindow.get_visible():
            return True
        x, y, f = root.get_pointer()
        if x < 2 and y < 2:
            print x, y
            mainwindow.show()
            pass
        return True
    gobject.timeout_add(1000, poll, root, mainwindow)
    pass


USAGE = '''expose.py - a full-screen task-switcher liks Apple's Expose

Usage:
    expose.py -h, --help        Show help options
    expose.py -v, --version     Show version information
    expose.py -w, --watch       Watch and trigger Expose while you rest the mouse in northwest edge of the screen
'''

def main():
    import getopt
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'hvw', ['version', 'help', 'watch'])
        pass
    except:
        print USAGE
        return sys.exit(1)
    global WATCH
    for o, v in opts:
        if o in ('-h', '--help'):
            print USAGE
            return
        elif o in ('-v', '--version'):
            print __version__
            return
        elif o in ('-w', '--watch'):
            WATCH = True
        pass
    ##
    gtk.gdk.threads_init()
    ## Disable on Compiz or Gnome3
    screen = gtk.gdk.screen_get_default()
    window_manager_name = screen.get_window_manager_name().lower() if screen else ''
    if window_manager_name in ['mutter', 'compiz']:
        print 'Disable on Compiz or Gnome3'
        return
    ##
    mainwindow = MainWindow()
    if WATCH:
        gobject.timeout_add(100, poll_pointer, mainwindow)
        pass
    else:
        mainwindow.show()
        pass
    gtk.main()
    pass

if __name__=="__main__":
    main()


